#include <logger.h>

#include "iec101transfer.h"
#include "phylink.h"
#include "dx_type.h"
#include "iot_debug.h"
#include "tools/m_sleep.h"

static int readAndWrite(IPhyLink *uart, const uint8_t *tx_data, uint8_t tx_len, uint8_t *rx_buf, uint8_t *rx_len);


IEC101Transfer::IEC101Transfer()
{

}

uint8_t IEC101Transfer::calcCheckSum(const uint8_t *buf, uint8_t size)
{
    uint8_t checkSum = 0;
    for(uint8_t i = 0; i < size; i++)
        checkSum += buf[i];

    return checkSum;
}

bool IEC101Transfer::sendCmdAndRecv(IPhyLink *uart, const uint8_t *tx_buf, uint8_t tx_len, uint8_t *recv_buf, uint8_t *rx_len)
{
    return (readAndWrite(uart, tx_buf, tx_len, recv_buf, rx_len) == ERR_NO_ERROR);
}


static int recvData(IPhyLink *uart, uint8_t *rx_buf, uint8_t rx_len, uint32_t timeout, uint8_t *ret_len)
{
    int err_code = ERR_NO_ERROR;
    uint32_t n = uart->read(rx_buf, rx_len, timeout);
    if (rx_len != n)
        err_code = ERR_RECV_TIMEOUT;
    if (ret_len)
        *ret_len = n & 0xFF;
    return err_code;
}

static int readAndWrite(
                 IPhyLink *uart,
                 const uint8_t *tx_data, uint8_t tx_len,
                 uint8_t *rx_buf, uint8_t *rx_len)
{
    const ComSettings * settings = uart->comSettings();
    uart->lock();
    if (tx_data && tx_len) {
        if (settings->dump_msg) {
            printf("[TX %d,%s] ", tx_len, logTime().c_str());
            for (int i=0; i<tx_len; i++)
                printf("%02X ", tx_data[i]);
            printf("\n");
        }

        uart->clear();
        if (tx_len != uart->write(tx_data, tx_len)) {
            uart->unlock();
            return ERR_SEND_FAILED;
        }
    }
    uint32_t wait_time = settings->recv_frame_timeout_ms;
    if (wait_time < 1)
        wait_time = 1;
    uint8_t & n = *rx_len;
    uint8_t ret_len = 0;
    n = 0;
    int err_code = recvData(uart, rx_buf, 1, wait_time, &ret_len);
    n += ret_len;
    if (err_code != ERR_NO_ERROR)
        goto exitCode;
    wait_time = settings->recv_char_timeout_ms;
    if (wait_time < 1)
        wait_time = 1;
    if (rx_buf[0] == 0x10) {
        uint8_t size = settings->iec101.link_addr_len == 1 ? 4:5;
        err_code = recvData(uart, rx_buf+n, size, wait_time, &ret_len);
        n += ret_len;
        if (err_code != ERR_NO_ERROR)
            goto exitCode;
        if (rx_buf[size] != 0x16 || IEC101Transfer::calcCheckSum(rx_buf+1, size-2)!=rx_buf[size-1]) {
            err_code = ERR_RECV_DATA_ERROR;
            goto exitCode;
        }
    }
    else if (rx_buf[0] == 0x68) {
        err_code = recvData(uart, rx_buf+n, 3, wait_time, &ret_len);
        n += ret_len;
        if (err_code != ERR_NO_ERROR) {
            goto exitCode;
        }
        if (rx_buf[3] != 0x68 || rx_buf[1] != rx_buf[2]) {
            err_code = ERR_RECV_DATA_ERROR;
            goto exitCode;
        }

        err_code = recvData(uart, rx_buf+n, rx_buf[1]+2, wait_time, &ret_len);
        n += ret_len;
        if (err_code != ERR_NO_ERROR) {
            goto exitCode;
        }
        if (rx_buf[n-1] != 0x16 || IEC101Transfer::calcCheckSum(rx_buf+4, rx_buf[2])!=rx_buf[n-2]) {
            err_code = ERR_RECV_DATA_ERROR;
            goto exitCode;
        }
    }
    else if (rx_buf[0] != 0xE5) {
        err_code = ERR_RECV_DATA_ERROR;
    }

exitCode:
    if (err_code == ERR_RECV_DATA_ERROR || err_code == ERR_CRC_CHECK_FAILED) {
        if (settings->dump_msg) {
            printf("<RX error,%s> ", logTime().c_str());
            for (uint16_t i=0; i<n; i++) {
                printf("%02X ", rx_buf[i]);
            }
        }
        int err = ERR_NO_ERROR;
        do {
            err = recvData(uart, rx_buf, 128, wait_time, &ret_len);
            if (settings->dump_msg) {
                for (uint8_t i=0; i<ret_len; i++) {
                    printf("%02X ", rx_buf[i]);
                }
            }
        } while(ret_len && err!=ERR_RECV_TIMEOUT);
        if (settings->dump_msg) {
            printf("\n");
        }
        uart->unlock();
    }
    else {
        uart->unlock();
        if (settings->dump_msg) {
            printf("<RX %d,%s> ", n, logTime().c_str());
            for (uint8_t i=0; i<n; i++)
                printf("%02X ", rx_buf[i]);
            if (err_code != ERR_RECV_DATA_ERROR && err_code != ERR_CRC_CHECK_FAILED) {
                printf("\n");
            }
        }
    }
    return err_code;
}
